﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components;
using Workflows.Components.Entities.Properties;
using Workflows.Components.Entities.Providers;

namespace Workflows.Components.Entities.StreamCollapse
{
    [Serializable]
    public class CollapseComponent:WorkflowComponentBase
    {
        #region props

        private CollapseInstruction _Instruction;
        public override InstructionBase Instruction
        {
            get { return this._Instruction; }
            set { this._Instruction=(CollapseInstruction) value; }
        }

        private CollapseOutput _Output;
        public override ResultBase Output
        {
            get { return this._Output; }
            set { this._Output=(CollapseOutput)value; }
        }

        /// <summary></summary>
        public string IdAttributeName
        {
            get
            {
                return this._Instruction.IdAttributeName;
            }
            set
            {
                this._Instruction.IdAttributeName = value;
            }
        }

        /// <summary></summary>
        public string CollapsedIdAttributeName
        {
            get
            {
                return this._Instruction.CollapsedIdAttributeName;
            }
            set
            {
                this._Instruction.CollapsedIdAttributeName = value;
            }
        }

        /// <summary></summary>
        public string PartitionedEntityProviderFilePath
        {
            get
            {
                return this._Instruction.PartitionedEntityProviderFilePath;
            }
            set
            {
                this._Instruction.PartitionedEntityProviderFilePath = value;
            }
        }

        /// <summary></summary>
        public DataProviderType DbProvider
        {
            get
            {
                return this._Instruction.DbProvider;
            }
            set
            {
                this._Instruction.DbProvider = value;
            }
        }

        /// <summary></summary>
        public string ConnStr
        {
            get
            {
                return this._Instruction.ConnStr;
            }
            set
            {
                this._Instruction.ConnStr = value;
            }
        }

        /// <summary></summary>
        public string SelectSql
        {
            get
            {
                return this._Instruction.SelectSql;
            }
            set
            {
                this._Instruction.SelectSql = value;
            }
        }

        /// <summary></summary>
        public List<AttributeMetaData> AttrMetaDatas
        {
            get
            {
                return this._Instruction.AttrMetaDatas;
            }
            set
            {
                this._Instruction.AttrMetaDatas = value;
            }
        }

        /// <summary></summary>
        public string CollapseCalculatorTypeName
        {
            get
            {
                return this._Instruction.CollapseCalculatorTypeName;
            }
            set
            {
                this._Instruction.CollapseCalculatorTypeName = value;
            }
        }

        /// <summary>
        /// add to overall score when sum of native attr comparison score 
        /// is greater than threshold
        /// </summary>
        public double NativeAttributeThreshold
        {
            get
            {
                return this._Instruction.NativeAttributeThreshold;
            }
            set
            {
                this._Instruction.NativeAttributeThreshold = value;
            }
        }

        /// <summary>
        /// add to overall score when sum of associative attr comparison score 
        /// is greater than threshold
        /// </summary>
        public double AssociativeAttributeThreshold
        {
            get
            {
                return this._Instruction.AssociativeAttributeThreshold;
            }
            set
            {
                this._Instruction.AssociativeAttributeThreshold = value;
            }
        }

        /// <summary>retrieve linkage distance that are stored in db</summary>
        public double LinkageAttributeThreshold
        {
            get
            {
                return this._Instruction.LinkageAttributeThreshold;
            }
            set
            {
                this._Instruction.LinkageAttributeThreshold = value;
            }
        }
        
        /// <summary>
        /// only collapse when overall score is greater than threshold
        /// </summary>
        public double CollapseScoreThreshold
        {
            get
            {
                return this._Instruction.CollapseScoreThreshold;
            }
            set
            {
                this._Instruction.CollapseScoreThreshold = value;
            }
        }

        /// <summary></summary>
        public string CollapseResultFilePath
        {
            get
            {
                return this._Instruction.CollapseResultFilePath;
            }
            set
            {
                this._Instruction.CollapseResultFilePath = value;
            }
        }

        /// <summary></summary>
        public string TgtConnStr
        {
            get
            {
                return this._Instruction.TgtConnStr;
            }
            set
            {
                this._Instruction.TgtConnStr = value;
            }
        }

        /// <summary></summary>
        public string CollapseInstanceTable
        {
            get
            {
                return this._Instruction.CollapseInstanceTable;
            }
            set
            {
                this._Instruction.CollapseInstanceTable = value;
            }
        }

        /// <summary></summary>
        public Dictionary<string, string> CollapseInstanceFieldMappings
        {
            get
            {
                return this._Instruction.CollapseInstanceFieldMappings;
            }
            set
            {
                this._Instruction.CollapseInstanceFieldMappings = value;
            }
        }

        /// <summary></summary>
        public string CollapseMappingTable
        {
            get
            {
                return this._Instruction.CollapseMappingTable;
            }
            set
            {
                this._Instruction.CollapseMappingTable = value;
            }
        }

        /// <summary></summary>
        public string CollapseMappingEntityIDField
        {
            get
            {
                return this._Instruction.CollapseMappingEntityIDField;
            }
            set
            {
                this._Instruction.CollapseMappingEntityIDField = value;
            }
        }

        /// <summary></summary>
        public string CollapseMappingCollapseIDField
        {
            get
            {
                return this._Instruction.CollapseMappingCollapseIDField;
            }
            set
            {
                this._Instruction.CollapseMappingCollapseIDField = value;
            }
        }

        /// <summary></summary>
        public string CollapseMappingScoreField
        {
            get
            {
                return this._Instruction.CollapseMappingScoreField;
            }
            set
            {
                this._Instruction.CollapseMappingScoreField = value;
            }
        }

        /// <summary></summary>
        public List<List<string>> FilterAttributes
        {
            get
            {
                return this._Instruction.FilterAttributes;
            }
            set
            {
                this._Instruction.FilterAttributes = value;
            }
        }
        #endregion

        public CollapseComponent():base()
        {
            this.ComponentActivityType = WorkflowActivityType.SimpleActivity;
            this.ComponentCategory = WorkflowComponentCategory.Entity_Relations.ToString();
            this.ComponentName = "Collapse";
            this.Description = "Collapse entity based on attribute statistics";
            this.ComponentIcon = Resources.calc_SC;
            this.ComponentImage = Resources.calc;

            this._Instruction = 
                new CollapseInstruction(this.ComponentID, Guid.Empty, 
                    string.Empty, string.Empty, 
                    string.Empty, DataProviderType.MSSQL,
                    string.Empty, string.Empty,
                    new List<AttributeMetaData>(), string.Empty, 
                    0.35, 0.50, 0.50, 0.75,
                    string.Empty, string.Empty, 
                    string.Empty, new Dictionary<string,string>(),
                    string.Empty,string.Empty,string.Empty,string.Empty,
                    new List<List<string>>());
            this._Output = 
                new CollapseOutput(
                    this.ComponentID, Guid.Empty, 0, 0, 
                    string.Empty, string.Empty, string.Empty);
        }

        public override WorkflowComponentBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                CollapseComponent component = base.Instantiate(xNode) as CollapseComponent;

                component.IdAttributeName =
                    XmlDataUtil.GetAttributeValue(xNode, "IdAttributeName", "");
                component.CollapsedIdAttributeName =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapsedIdAttributeName", "");
                component.PartitionedEntityProviderFilePath =
                    XmlDataUtil.GetAttributeValue(xNode, "PartitionedEntityProviderFilePath", "");
                component.DbProvider =
                    (DataProviderType)
                    int.Parse(
                        XmlDataUtil.GetAttributeValue(
                            xNode, "DbProvider",
                            ((int)DataProviderType.MSSQL).ToString()));
                component.ConnStr =
                    XmlDataUtil.GetAttributeValue(xNode, "ConnStr", "");
                component.SelectSql =
                    XmlDataUtil.GetAttributeValue(xNode, "SelectSql", "");

                // attribute 
                XmlNodeList attrNodes = xNode.SelectNodes("Attributes/Attribute");
                if (attrNodes != null && attrNodes.Count > 0)
                {
                    component.AttrMetaDatas = new List<AttributeMetaData>();
                    foreach (XmlNode attrNode in attrNodes)
                    {
                        string assemblyName = XmlDataUtil.GetAttributeValue(attrNode, "AssemblyName", "");
                        string typeNmae = XmlDataUtil.GetAttributeValue(attrNode, "TypeName", "");
                        AttributeType attrType =
                            (AttributeType)
                            int.Parse(XmlDataUtil.GetAttributeValue(attrNode, "AttributeType",
                                                                    ((int)AttributeType.NativeAttribute).ToString()));
                        double posW = double.Parse(XmlDataUtil.GetAttributeValue(attrNode, "PositiveWeight", "1.0"));
                        double negW = double.Parse(XmlDataUtil.GetAttributeValue(attrNode, "NegativeWeight", "-1.0"));
                        bool isExternalAttr =
                            bool.Parse(XmlDataUtil.GetAttributeValue(attrNode, "IsExternal", false.ToString()));
                        DataProviderType extDbProvider =
                            (DataProviderType)
                            int.Parse(XmlDataUtil.GetAttributeValue(attrNode, "ExtDbProvider",
                                                                    ((int)DataProviderType.MSSQL).ToString()));
                        string extConnStr = XmlDataUtil.GetAttributeValue(attrNode, "ExtConnStr", "");
                        string extSql = XmlDataUtil.GetAttributeValue(attrNode, "ExtSql", "");
                        
                        Dictionary<string, string> comparerSettings = new Dictionary<string, string>();
                        XmlNodeList settingNodes = attrNode.SelectNodes("Setting");
                        if (settingNodes != null && settingNodes.Count > 0)
                        {
                            foreach (XmlNode settingNode in settingNodes)
                            {
                                string name = XmlDataUtil.GetAttributeValue(settingNode, "Name", "");
                                string value = XmlDataUtil.GetAttributeValue(settingNode, "Value", "");
                                comparerSettings.Add(name, value);
                            }
                        }

                        if (attrType == AttributeType.CompositeAttribute)
                        {
                            Dictionary<string, string> argAttrBindings = new Dictionary<string, string>();
                            XmlNodeList attrNameNodes = attrNode.SelectNodes("Attribute");
                            foreach (XmlNode attrNameNode in attrNameNodes)
                            {
                                string argName = XmlDataUtil.GetAttributeValue(attrNameNode, "ArgumentName", "");
                                string attrName = XmlDataUtil.GetAttributeValue(attrNameNode, "AttributeName", "");
                                argAttrBindings.Add(argName, attrName);
                            }
                            
                            AttributeMetaData attrMetaData = new AttributeMetaData(
                                argAttrBindings, assemblyName, typeNmae, 
                                comparerSettings, posW, negW,
                                isExternalAttr, extDbProvider, extConnStr, extSql);
                            component.AttrMetaDatas.Add(attrMetaData);
                        }
                        else
                        {
                            string attrName = XmlDataUtil.GetAttributeValue(attrNode, "AttributeName", "");
                            AttributeMetaData attrMetaData = new AttributeMetaData(
                                attrName, attrType, assemblyName, typeNmae, 
                                comparerSettings, posW, negW,
                                isExternalAttr, extDbProvider, extConnStr, extSql);
                            component.AttrMetaDatas.Add(attrMetaData);
                        }
                    }
                }


                component.CollapseCalculatorTypeName =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseCalculatorTypeName", "");

                component.NativeAttributeThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "NativeAttributeThreshold", "0.0"));
                component.AssociativeAttributeThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "AssociativeAttributeThreshold", "0.0"));
                component.LinkageAttributeThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "LinkageAttributeThreshold", "0.5"));
                component.CollapseScoreThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "CollapseScoreThreshold", "0.0"));
                
                
                component.CollapseResultFilePath =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseResultFilePath", "");
                component.TgtConnStr =
                                    XmlDataUtil.GetAttributeValue(xNode, "TgtConnStr", "");
                component.CollapseInstanceTable =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseInstanceTable", "");
                XmlNodeList tgtTableFieldMappingNodes = xNode.SelectNodes("TgtFieldMappings/TgtFieldMapping");
                if (tgtTableFieldMappingNodes != null && tgtTableFieldMappingNodes.Count > 0)
                {
                    component.CollapseInstanceFieldMappings = new Dictionary<string, string>();
                    foreach (XmlNode fieldMappingNode in tgtTableFieldMappingNodes)
                    {
                        string srcField = XmlDataUtil.GetAttributeValue(fieldMappingNode, "SrcField", "");
                        string tgtField = XmlDataUtil.GetAttributeValue(fieldMappingNode, "TgtField", "");
                        component.CollapseInstanceFieldMappings.Add(srcField, tgtField);
                    }
                }
                component.CollapseMappingTable =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseMappingTable", "");
                component.CollapseMappingEntityIDField =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseMappingEntityIDField", "");
                component.CollapseMappingCollapseIDField =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseMappingCollapseIDField", "");
                component.CollapseMappingScoreField =
                    XmlDataUtil.GetAttributeValue(xNode, "CollapseMappingScoreField", "");

                XmlNodeList filterNodes = xNode.SelectNodes("Filters/Filter");
                if (filterNodes != null && filterNodes.Count > 0)
                {
                    component.FilterAttributes = new List<List<string>>();
                    foreach (XmlNode filterNode in filterNodes)
                    {
                        XmlNodeList filterAttrNodes = filterNode.SelectNodes("Attribute");
                        if (FilterAttributes != null && filterAttrNodes.Count > 0)
                        {
                            List<string> attrNames = new List<string>();
                            foreach (XmlNode attrNode in filterAttrNodes)
                            {
                                attrNames.Add(XmlDataUtil.GetAttributeValue(attrNode, "AttributeName", ""));
                            }
                            component.FilterAttributes.Add(attrNames);
                        }
                    }
                }

                return component;
            }
            else
            {
                throw new Exception("Unable to instantiate email: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = base.Serialize(ref xDoc, ref parentNode);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "IdAttributeName", this.IdAttributeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapsedIdAttributeName", this.CollapsedIdAttributeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseCalculatorTypeName", this.CollapseCalculatorTypeName);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "PartitionedEntityProviderFilePath", this.PartitionedEntityProviderFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "DbProvider", ((int)this.DbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ConnStr", this.ConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SelectSql", this.SelectSql);
            
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "NativeAttributeThreshold", this.NativeAttributeThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "AssociativeAttributeThreshold", this.AssociativeAttributeThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "LinkageAttributeThreshold", this.LinkageAttributeThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseScoreThreshold", this.CollapseScoreThreshold.ToString());
            
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseCalculatorTypeName", this.CollapseCalculatorTypeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseResultFilePath", this.CollapseResultFilePath);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtConnStr", this.TgtConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseInstanceTable", this.CollapseInstanceTable);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseMappingTable", this.CollapseMappingTable);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseMappingEntityIDField",
                                        this.CollapseMappingEntityIDField);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseMappingCollapseIDField",
                                        this.CollapseMappingCollapseIDField);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "CollapseMappingScoreField", this.CollapseMappingScoreField);

            // attributes
            if (this.AttrMetaDatas != null && this.AttrMetaDatas.Count > 0)
            {
                XmlNode attrRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Attributes");
                foreach (AttributeMetaData attributeMetaData in this.AttrMetaDatas)
                {
                    XmlNode attrNode = XmlDataUtil.AddElement(ref xDoc, attrRootNode, "Attribute");
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AssemblyName", attributeMetaData.ComparerAssemblyName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "TypeName", attributeMetaData.ComparerTypeName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AttributeType",
                                                ((int)attributeMetaData.AttributeType).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "PositiveWeight", attributeMetaData.PositiveMatchWeight.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "NegativeWeight", attributeMetaData.NegativeMatchWeight.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "IsExternal", attributeMetaData.IsExternalAttribute.ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtDbProvider", ((int)attributeMetaData.ExternalAttributeDbProvider).ToString());
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtConnStr", attributeMetaData.ExternalAttributeConnStr);
                    XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "ExtSql", attributeMetaData.ExternalAttributeSelectSql);

                    if (attributeMetaData.ComparerSettings != null && attributeMetaData.ComparerSettings.Count > 0)
                    {
                        foreach (string name in attributeMetaData.ComparerSettings.Keys)
                        {
                            XmlNode settingNode = XmlDataUtil.AddElement(ref xDoc, attrNode, "Setting");
                            XmlDataUtil.UpdateAttribute(ref xDoc, settingNode, "Name", name);
                            XmlDataUtil.UpdateAttribute(ref xDoc, settingNode, "Value", attributeMetaData.ComparerSettings[name]);
                        }
                    }

                    if (attributeMetaData.AttributeType == AttributeType.CompositeAttribute)
                    {
                        
                        foreach (string argName in attributeMetaData.ArgNameAttrNameBindings.Keys)
                        {
                            XmlNode attrNameNode = XmlDataUtil.AddElement(ref xDoc, attrNode, "Attribute");
                            XmlDataUtil.UpdateAttribute(ref xDoc, attrNameNode, "ArgumentName", argName);
                            XmlDataUtil.UpdateAttribute(ref xDoc, attrNameNode, "AttributeName",
                                                        attributeMetaData.ArgNameAttrNameBindings[argName]);
                        }
                    }
                    else
                    {
                        XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AttributeName", attributeMetaData.AttributeName);
                    }
                }
            }

            
            if (this.CollapseInstanceFieldMappings != null && this.CollapseInstanceFieldMappings.Count > 0)
            {
                XmlNode fieldMapRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "TgtFieldMappings");
                foreach (string srcField in this.CollapseInstanceFieldMappings.Keys)
                {
                    XmlNode fieldMapNode = XmlDataUtil.AddElement(ref xDoc, fieldMapRootNode, "TgtFieldMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, fieldMapNode, "SrcField", srcField);
                    XmlDataUtil.UpdateAttribute(ref xDoc, fieldMapNode, "TgtField", this.CollapseInstanceFieldMappings[srcField]);
                }
            }

            if (this.FilterAttributes != null && this.FilterAttributes.Count > 0)
            {
                XmlNode filterRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "Filters");
                for (int i = 0; i < this.FilterAttributes.Count; i++)
                {
                    XmlNode filterNode = XmlDataUtil.AddElement(ref xDoc, filterRootNode, "Filter");
                    List<string> filterAttrs = this.FilterAttributes[i];
                    foreach (string attrName in filterAttrs)
                    {
                        XmlNode attrNode = XmlDataUtil.AddElement(ref xDoc, filterNode, "Attribute");
                        XmlDataUtil.UpdateAttribute(ref xDoc, attrNode, "AttributeName", attrName);
                    }
                }
            }

            return xNode;
        }
    }
}
